#include "queue_buffer.h"
#include "queue_buffer_cfg.h"
#include "util_errno.h"
#include <string.h>	/* memset */

static queue_entry_t g_group_queue_entry[QBUF_GROUP_COUNT];	/* 队列的入口 */
static qbuf_t g_qbuf[QBUF_MAX_COUNT + 1]; /* 所有缓存节点 */
static int g_buf_id;	/* 缓存的循环序号 */

/**
 * \brief	将系统中所有缓存都推到已释放队列中，等待进一步初始化
 */
void qbuf_init(void)
{
    int i;
    qbuf_t *buf;
    queue_t *pqueue;

    g_buf_id = 0;

	/* 初始化所有队列入口 */
    for (i = 0; i < QBUF_GROUP_COUNT; i++) {
        queue_entry_init(&g_group_queue_entry[i]);
    }

	/* 清除所有缓存参数 */
    memset(g_qbuf, 0, sizeof(g_qbuf));

	/* 将所有缓存变量都推入到已释放队列中，暂不分组 */
    for (i = 0; i < QBUF_MAX_COUNT; i++) {
        buf = &g_qbuf[i];
        buf->mark = QBUF_MARK;
        pqueue = &(buf->queue);
        init_list_head(&(pqueue->node));
        pqueue->queue_entry = &g_group_queue_entry[QBUF_GROUP_FREE];
        queue_free(pqueue);
    }
}

/**
 * \brief	初始化一个缓存组，分配好组内所有的缓存
 * \param	qg:	、该组的配置参数
 * \param	queue_entry:	该组的类型
 */
int qbuf_group_cfg(qbuf_group_cfg_t *qg, uint32_t queue_entry)
{
    int i;
    queue_entry_t *pqe;		/* 当前缓存组的队列入口 */
    qbuf_t *buf, *oldbuf;	/* 一个缓存 */
    queue_t *pqueue;		/* 一个缓存的节点 */

    if (!qg || queue_entry <= QBUF_GROUP_FREE
            || queue_entry >= QBUF_GROUP_COUNT ) {
        return -EPERM;
    }

    pqe = &(g_group_queue_entry[queue_entry]);	/* 要推入的缓存组 */

    /* 释放前一次销毁整个缓存队列后，某些仍在被分配的队列中的缓存；
	   将所有之前的queue_entry组的缓存放回到已释放队列中；
	   调用本函数前，其实已经调用过了qbuf_init，所有的缓存都放在
	   已释放队列中了 */
    for (i = 0; i < QBUF_MAX_COUNT; i++) {
        buf = &g_qbuf[i];
        pqueue = &(buf->queue);
        if (pqueue->queue_entry == pqe) {	/* 该缓存节点所在的队列入口 */
            pqueue->queue_entry = &g_group_queue_entry[QBUF_GROUP_FREE];	/* 释放 */
            queue_free(pqueue);
        }
    }

    /* 重新初始化该组缓存的入口 */
    queue_entry_destroy(pqe);
    queue_entry_init(pqe);

    /* 从所有已释放缓存变量中获取自己组需要的变量，然后推入到自己的组中 */
    oldbuf = NULL;
    for (i = 0; i< qg->count; i++) {	/* 遍历该组缓存总个数 */
        pqueue = queue_alloc(&g_group_queue_entry[QBUF_GROUP_FREE]);
        if (!pqueue) {
            return -ENOMEM;
        }

		/* 初始化每个缓存，赋值地址 */
        buf = container_of(pqueue, qbuf_t, queue);

        buf->flags = 0;
        buf->offset = 0;
        buf->len = 0;
        buf->size = qg->unitsize;

		///TODO: 当qg->type == QBUF_TYPE_MALLOC时，将缓存空间替换为在使用时malloc，释放时free
        if (!oldbuf) {
            buf->addr = qg->addr;
        } else {
            buf->addr = oldbuf->addr + qg->unitsize;
        }
        oldbuf = buf;

        pqueue->queue_entry = pqe;
        queue_free(pqueue);	/* 推入到自己组中 */
    }

    return 0;
}

/**
 * \brief 从该组内获取一个缓存，并初始化缓存的参数，这个缓存在用完后要记得释放回去
 */
qbuf_t *qbuf_alloc(uint32_t group)
{
    qbuf_t *buf;
    queue_t *pqueue;
    queue_entry_t *pqe;

    if (group <= QBUF_GROUP_FREE
            || group >= QBUF_GROUP_COUNT ) {
        return NULL;
    }

    pqe = &(g_group_queue_entry[group]);
    pqueue = queue_alloc(pqe);
    if (!pqueue) {
        return NULL;
    }

    buf = container_of(pqueue, qbuf_t, queue);

    buf->group = group;
    buf->flags = 0;
    buf->offset = 0;
    buf->len = 0;

    g_buf_id++;
    if (g_buf_id == 0)
        g_buf_id = 1;
    buf->id = g_buf_id;

    return buf;
}

/**
 * \brief	缓存用完后重新释放回该组的已释放队列中
 */
int qbuf_free(qbuf_t *buf)
{
    int res;

    if (!QBUF_ISVALID(buf))
        return -EPERM;

    res = queue_free(&buf->queue);
    if (res == 0) {
        buf->group = QBUF_GROUP_FREE;
    }

    return res;
}

/**
 * \brief	将数据发送到该组已使用队列中
 */
int qbuf_put(qbuf_t *buf)
{
    int res;

    if (!QBUF_ISVALID(buf))
        return -EPERM;

    res = queue_put(NULL, &(buf->queue), QUEUE_USED);

    return res;
}

/**
 * \brief	从该组已使用队列中获取一个有有效数据的缓存
 */
qbuf_t *qbuf_get(uint32_t group)
{
    qbuf_t *buf;
    queue_t *pqueue;
    queue_entry_t *pqe;

    if (group <= QBUF_GROUP_FREE
        || group >= QBUF_GROUP_COUNT ) {
        return NULL;
    }

    pqe = &g_group_queue_entry[group];
    pqueue = queue_get(pqe, QUEUE_USED);
    if (!pqueue) {
        return NULL;
    }
    buf = container_of(pqueue, qbuf_t, queue);

    return buf;
}
